﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace EdgeStatz.Controls
{
    public partial class AvailabilityControl : UserControl
    {
        private AvailabilityContactCollection contacts = new AvailabilityContactCollection();

        #region Controls
        Label[] lbls_header = new Label[0];
        Label[] lbls_row_names = new Label[0];
        AvailabilityPanel[] panels_rows = new AvailabilityPanel[0];
        #endregion

        #region Properties

        private DateTime start_date = DateTime.Now;
        private AvailabilityMode mode = AvailabilityMode.Week;
        private bool mutil_select = false;
        private bool allow_edit = true;
        private bool allow_add = true;
        private bool allow_delete = true;
        private Color row_background = Color.AliceBlue;
        private Color row_foreground = Color.Black;
        private Color row_alternate_background = Color.White;
        private Color row_alternate_foreground = Color.Black;
        private Font row_font = DefaultFont;
        private Color header_background = SystemColors.GradientActiveCaption;
        private Color header_foreground = Color.DarkBlue;
        private Color selection_background = Color.Black;
        private Color selection_foreground = Color.White;
        private Font header_font = DefaultFont;
        private BorderStyle header_border_style = BorderStyle.None;
        private Point hit_location = new Point(-1, -1);

        /// <summary>
        /// Gets or sets a value indicating whether the option to add new contacts is displayed to the user
        /// </summary>
        public bool AllowUserToAddContacts { get { return allow_add; } set { allow_add = value; } }
        /// <summary>
        /// Gets or sets a value indicating whether the option to edit existing contacts is displayed to the user
        /// </summary>
        public bool AllowUserToEditContacts { get { return allow_edit; } set { allow_edit = value; } }
        /// <summary>
        /// Gets or sets a value indicating whether the option to delete existing contacts is displayed to the user
        /// </summary>
        public bool AllowUserToDeleteContacts { get { return allow_delete; } set { allow_delete = value; } }
        /// <summary>
        /// Gets or sets a value indicating whether multiple contacts can be selected.
        /// </summary>
        public bool MutilSelect { get { return mutil_select; } set { mutil_select = value; } }
        /// <summary>
        /// Gets or sets the background colour of each row.
        /// </summary>
        public Color RowBackgroundColor { get { return row_background; } set { row_background = value; } }
        /// <summary>
        /// Gets or sets the background colour of each alternating row.
        /// </summary>
        public Color AlternateRowBackgroundColor { get { return row_alternate_background; } set { row_alternate_background = value; } }
        /// <summary>
        /// Gets or sets the foreground colur of each row
        /// </summary>
        public Color RowColor { get { return row_foreground; } set { row_foreground = value; } }
        /// <summary>
        /// Gets or sets the foreground colour of each alternating row.
        /// </summary>
        public Color AlternateRowColor { get { return row_alternate_foreground; } set { row_alternate_foreground = value; } }
        /// <summary>
        /// Gets or sets the background colour of the header row.
        /// </summary>
        public Color HeaderBackgroundColor { get { return header_background; } set { header_background = value; } }
        /// <summary>
        /// Gets or sets the foreground colour of the header row.
        /// </summary>
        public Color HeaderColor { get { return header_foreground; } set { header_foreground = value; } }
        /// <summary>
        /// Gets or sets the background colour of the selected row(s).
        /// </summary>
        public Color SelectedRowBackgroundColor { get { return selection_background; } set { selection_background = value; } }
        /// <summary>
        /// Gets or sets the foreground colour of the selected row(s).
        /// </summary>
        public Color SelectedRowColor { get { return selection_foreground; } set { selection_foreground = value; } }
        /// <summary>
        /// Gets or sets the font of the header row.
        /// </summary>
        public Font HeaderFont { get { return header_font; } set { header_font = value; } }
        /// <summary>
        /// Gets or sets the font of each row.
        /// </summary>
        public Font RowFont { get { return row_font; } set { row_font = value; } }
        /// <summary>
        /// Gets or sets the border style of the header row.
        /// </summary>
        public BorderStyle HeaderBorderStyle { get { return header_border_style; } set { header_border_style = value; } }
        /// <summary>
        /// Gets or sets the start date of the currently displayed set of dates.
        /// </summary>
        public DateTime StartDate
        {
            get { return start_date; }
            set { start_date = value;
                //RefreshControl(false); 
            }
        }
        /// <summary>
        /// Gets or sets the display mode of the control.
        /// </summary>
        public AvailabilityMode Mode 
        { 
            get { return mode; } 
            set { this.mode = value; RefreshControl(false); } 
        }
        /// <summary>
        /// Gets or sets the collection of contacts.
        /// </summary>
        public AvailabilityContactCollection Contacts 
        { 
            get { return contacts; }
            set { contacts = value; RefreshControl(false); } 
        }
        /// <summary>
        /// Gets the number of contacts currently in the collection.
        /// </summary>
        public int ContactCount { get { return contacts.Count; } }

        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor.
        /// The Mode will be set to Week, and the selected to Now.
        /// </summary>
        public AvailabilityControl()
            : this(DateTime.Now, AvailabilityMode.Week) { }

        public AvailabilityControl(DateTime date)
            : this(date, AvailabilityMode.Week) { }

        public AvailabilityControl(AvailabilityMode mode)
            : this(DateTime.Now, mode) { }
        
        public AvailabilityControl(DateTime date, AvailabilityMode mode)
        {
            InitializeComponent();
            this.mode = mode;
            dtp_Date.Value = date;
            RefreshControl(true);
        }
        #endregion

        #region Refresh Control
        private void RefreshControl(bool redraw)
        {
            Refresh_StartDate();
            switch (mode)
            {
                case AvailabilityMode.Day:
                    Refresh_Header_Day();
                    break;
                case AvailabilityMode.Week:
                    Refresh_Header_Week();
                    break;
                case AvailabilityMode.Month:
                    Refresh_Header_Month();
                    break;
                case AvailabilityMode.Year:
                    Refresh_Header_Year();
                    break;
            }
            RefreshContacts(redraw);
        }

        private void Refresh_StartDate() 
        {
            start_date = new DateTime(start_date.Year, start_date.Month, start_date.Day, 0, 0, 0);
            switch (mode)
            {
                case AvailabilityMode.Week:
                    if (start_date.DayOfWeek == DayOfWeek.Monday)
                        break;
                    else
                    {
                        while (start_date.DayOfWeek != DayOfWeek.Monday)
                            start_date = start_date.AddDays(-1.0);
                    }
                    break;
                case AvailabilityMode.Month:
                    start_date = new DateTime(start_date.Year, start_date.Month, 1, 0, 0, 0);
                    break;
                case AvailabilityMode.Year:
                    start_date = new DateTime(start_date.Year, 1, 1, 0, 0, 0);
                    break;
            }
            dtp_Date.Value = start_date;
        }

        private void Refresh_Header_Day() 
        {
            if (table_header.ColumnCount != 25)
            {

                table_header.Controls.Clear();
                lbls_header = new Label[24];
                table_header.ColumnStyles.Clear();
                table_header.ColumnCount = 25;
                for (int i = 0; i < 24; i++)
                {
                    table_header.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F / 24F));
                    lbls_header[i] = GetHeaderLabel(i.ToString() + ":00");
                    table_header.Controls.Add(lbls_header[i], i, 0);
                }
                table_header.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 0F));
            }     
        }

        private void Refresh_Header_Week() 
        {
            if (table_header.ColumnCount != 8)
            {
                table_header.Controls.Clear();
                lbls_header = new Label[7];
                table_header.ColumnCount = 8;
                table_header.ColumnStyles.Clear();
                lbls_header[0] = GetHeaderLabel("Monday");
                lbls_header[1] = GetHeaderLabel("Tuesday");
                lbls_header[2] = GetHeaderLabel("Wednesday");
                lbls_header[3] = GetHeaderLabel("Thursday");
                lbls_header[4] = GetHeaderLabel("Friday");
                lbls_header[5] = GetHeaderLabel("Saturday");
                lbls_header[6] = GetHeaderLabel("Sunday");
                for (int i = 0; i < 7; i++)
                {
                    table_header.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F / 7F));
                    table_header.Controls.Add(lbls_header[i], i, 0);
                }
                table_header.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 0F));
            }
        }

        private void Refresh_Header_Month() 
        {
            int max_days = new DateTime(start_date.Year, start_date.Month, 1).AddMonths(1).AddDays(-1.0).Day;
            if (table_header.ColumnCount != max_days + 1)
            {
                table_header.Controls.Clear();
                table_header.ColumnCount = max_days + 1;
                table_header.ColumnStyles.Clear();
                lbls_header = new Label[max_days];
                for (int i = 0; i < max_days; i++)
                {
                    table_header.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F / max_days));
                    lbls_header[i] = GetHeaderLabel((i + 1).ToString());
                    table_header.Controls.Add(lbls_header[i],i,0);
                }
                table_header.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 0F));
            }
        }

        private void Refresh_Header_Year() 
        {
            if (table_header.ColumnCount != 13)
            {
                table_header.Controls.Clear();
                table_header.ColumnCount = 13;
                table_header.ColumnStyles.Clear();
                lbls_header = new Label[12];
                lbls_header[0] = GetHeaderLabel("January");
                lbls_header[1] = GetHeaderLabel("February");
                lbls_header[2] = GetHeaderLabel("March");
                lbls_header[3] = GetHeaderLabel("April");
                lbls_header[4] = GetHeaderLabel("May");
                lbls_header[5] = GetHeaderLabel("June");
                lbls_header[6] = GetHeaderLabel("July");
                lbls_header[7] = GetHeaderLabel("August");
                lbls_header[8] = GetHeaderLabel("September");
                lbls_header[9] = GetHeaderLabel("October");
                lbls_header[10] = GetHeaderLabel("November");
                lbls_header[11] = GetHeaderLabel("December");
                for (int i = 0; i < 12; i++)
                {
                    table_header.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F / 12F));
                    table_header.Controls.Add(lbls_header[i], i, 0);
                }
                table_header.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 0F));
            }
        }

        private void RefreshContacts(bool redraw) 
        {            
            if (redraw || lbls_row_names.Length != ContactCount)
            {
                table_body.RowCount = ContactCount + 2;
                table_body.Controls.Clear();
                table_body.RowStyles.Clear();
                table_body.RowStyles.Add(new RowStyle(SizeType.Absolute, 25F));
                table_body.Controls.Add(table_header, 1, 0);
                lbls_row_names = new Label[ContactCount];
                panels_rows = new AvailabilityPanel[ContactCount];
                for (int i = 0; i < ContactCount; i++)
                {
                    table_body.RowStyles.Add(new RowStyle(SizeType.Absolute, 20F));
                    lbls_row_names[i] = GetContactLabel(contacts[i].Name, (i % 2 == 1));
                    if (contacts[i].Selected)
                    {
                        lbls_row_names[i].BackColor = selection_background;
                        lbls_row_names[i].ForeColor = selection_foreground;
                        lbls_row_names[i].Font = new Font(row_font, FontStyle.Bold);
                    }
                    table_body.Controls.Add(lbls_row_names[i], 0, i + 1);
                    RefreshTable(true, i);
                }
                table_body.RowStyles.Add(new RowStyle(SizeType.Absolute, 0F));
            }
            else 
            {
                for (int i = 0; i < ContactCount; i++)
                {
                    if (contacts[i].Selected)
                    {
                        lbls_row_names[i].BackColor = selection_background;
                        lbls_row_names[i].ForeColor = selection_foreground;
                        lbls_row_names[i].Font = new Font(row_font, FontStyle.Bold | FontStyle.Italic);
                    }
                    else
                    {
                        if (lbls_row_names[i].BackColor == selection_background)
                        {
                            lbls_row_names[i].BackColor = (i % 2 == 1) ? row_alternate_background : row_background;
                            lbls_row_names[i].ForeColor = (i % 2 == 1) ? row_alternate_foreground : row_foreground;
                            lbls_row_names[i].Font = row_font;
                        }
                    }
                    RefreshTable(false, i);
                }
            }
        }

        private void RefreshTable(bool redraw, int row)
        {
            int width = Convert.ToInt32(this.Size.Width * 0.8);
            if (redraw)
            {
                panels_rows[row] = new AvailabilityPanel();
                panels_rows[row].BackColor = (row % 2 == 1) ? row_alternate_background : row_background;
                table_body.Controls.Add(panels_rows[row], 1, row + 1);

                DateTime control_end;
                switch (mode)
                {
                    case AvailabilityMode.Day:
                        control_end = new DateTime(start_date.Year, start_date.Month, start_date.Day, 23, 59, 59);
                        break;
                    case AvailabilityMode.Week:
                        control_end = new DateTime(start_date.Year, start_date.Month, start_date.Day, 23, 59, 59).AddDays(6);
                        break;
                    case AvailabilityMode.Month:
                        control_end = new DateTime(start_date.Year, start_date.Month, 1, 23, 59, 59).AddMonths(1).AddDays(-1);
                        break;
                    case AvailabilityMode.Year:
                        control_end = new DateTime(start_date.Year, 1, 1, 23, 59, 59).AddYears(1).AddDays(-1);
                        break;
                    default:
                        goto case AvailabilityMode.Week;
                }
                long total_ticks = control_end.Ticks - start_date.Ticks;
                panels_rows[row].ClearRectangles();
                foreach (AvailabilityTask task in contacts[row].Tasks)
                {
                    decimal start_pos, end_pos;
                    long start_ticks = task.Start.Ticks - start_date.Ticks;
                    long end_ticks = task.End.Ticks - start_date.Ticks;

                    if (task.Start.CompareTo(start_date) < 0)
                        start_pos = 0.0m;
                    else if (task.Start.CompareTo(control_end) >= 0)
                        start_pos = 1.0m;
                    else
                        start_pos = (decimal)start_ticks / (decimal)total_ticks;

                    if (task.End.CompareTo(start_date) < 0)
                        end_pos = 0.0m;
                    else if (task.End.CompareTo(control_end) >= 0)
                        end_pos = 1.0m;
                    else
                        end_pos = (decimal)end_ticks / (decimal)total_ticks;

                    if (start_pos >= 1.0m || end_pos <= 0.0m || start_pos >= end_pos)
                        continue;
                    panels_rows[row].AddNewRectangle(start_pos, end_pos, task.Color, 
                        "Subject: " + task.Subject + "\nStart Time: " + task.Start.ToString() + "\n" + 
                        (task.AllDay ? "All day task: true" : "End Time: " + task.End.ToString() ) + "\nMessage: " + task.Message);
                }
            }
            else
            {
                panels_rows[row].Refresh();
            }
        }

        private Label GetHeaderLabel(string text)
        {
            Label lbl1 = new Label();
            lbl1.Dock = DockStyle.Fill;
            lbl1.Margin = new Padding(0);
            lbl1.Text = text;
            lbl1.TextAlign = ContentAlignment.MiddleCenter;
            lbl1.BackColor = header_background;
            lbl1.ForeColor = header_foreground;
            lbl1.Font = header_font;
            lbl1.BorderStyle = header_border_style;
            return lbl1;
        }

        private Label GetContactLabel(string text,bool alternate_row) 
        {
            Label lbl = new Label();
            lbl.Dock = DockStyle.Fill;
            lbl.Margin = new Padding(0);
            lbl.Text = text;
            lbl.TextAlign = ContentAlignment.MiddleLeft;
            lbl.BackColor = alternate_row ? row_alternate_background : row_background;
            lbl.ForeColor = alternate_row ? row_alternate_foreground : row_foreground;
            lbl.Font = row_font;
            lbl.Click += new EventHandler(table_body_Click);
            return lbl;
        }
        
        #endregion 

        #region UI Events
        private void b_Mode_Day_Click(object sender, EventArgs e)
        {
            if (Mode != AvailabilityMode.Day)
            {
                mode = AvailabilityMode.Day;
                StartDate = dtp_Date.Value;
                RefreshControl(true);
            }
        }

        private void b_Mode_Week_Click(object sender, EventArgs e)
        {
            if (Mode != AvailabilityMode.Week)
            {
                mode = AvailabilityMode.Week;
                StartDate = dtp_Date.Value;
                RefreshControl(true);
            } 
        }

        private void b_Mode_Month_Click(object sender, EventArgs e)
        {
            if (Mode != AvailabilityMode.Month)
            {
                mode = AvailabilityMode.Month;
                StartDate = dtp_Date.Value;
                RefreshControl(true);
            }
        }

        private void b_Mode_Year_Click(object sender, EventArgs e)
        {
            if (Mode != AvailabilityMode.Year)
            {
                mode = AvailabilityMode.Year;
                StartDate = dtp_Date.Value;
                RefreshControl(true);
            }
        }

        private void b_Date_Prev_Click(object sender, EventArgs e)
        {
            MoveToPrevious();
        }

        private void b_Date_Next_Click(object sender, EventArgs e)
        {
            MoveToNext();
        }
        
        private void b_GoTo_Click(object sender, EventArgs e)
        {
            StartDate = dtp_Date.Value;
            RefreshControl(true);
        }
        #endregion

        #region Context Menu

        void _tsmi_Delete_Click(object sender, EventArgs e)
        {
            throw new NotImplementedException();
        }

        void _tsmi_Edit_Click(object sender, EventArgs e)
        {
            throw new NotImplementedException();
        }

        void _tsmi_Add_Click(object sender, EventArgs e)
        {
            throw new NotImplementedException();
        }

        private Point table_body_HitTest(MouseEventArgs e)
        {
            int row = 0;
            int verticalOffset = table_body.DisplayRectangle.Y;

            foreach (int h in table_body.GetRowHeights())
            {
                int column = 0;
                int horizontalOffset = table_body.DisplayRectangle.X;
                foreach (int w in table_body.GetColumnWidths())
                {
                    Rectangle rectangle = new Rectangle(horizontalOffset, verticalOffset, w, h);
                    if (rectangle.Contains(e.Location))
                    {
                        return new Point(column, row);
                    }
                    horizontalOffset += w;
                    column++;
                }
                verticalOffset += h;
                row++;
            }
            return new Point(-1,-1);
        }

        private void table_body_Click(object sender, EventArgs e)
        {
            MouseEventArgs e_args = (MouseEventArgs)e;
            bool check_select = sender.GetType().Equals(typeof(Label));
            Point hit_info = new Point(-1, -1);
            
            if (check_select)
            {
                Point tmp = table_body.PointToClient(((Control)sender).PointToScreen(new Point(e_args.X, e_args.Y)));
                hit_info = table_body_HitTest(new MouseEventArgs(MouseButtons.None, 1, tmp.X, tmp.Y, 0));
            }
            else
                hit_info = table_body_HitTest(e_args);
            if (hit_info.Equals(new Point(-1, -1)))
                return;
            int column = hit_info.X;
            int row = hit_info.Y - 1;
            if (e_args.Button == MouseButtons.Left || e_args.Button == MouseButtons.Right)
            {
                if (check_select)
                {
                    bool ctrl_down = true;
                    if (!mutil_select || !ctrl_down)
                        for (int i = 0; i < ContactCount; i++)
                            contacts[i].Selected = false;
                    contacts[row].Selected = true;
                    RefreshControl(false);
                }
            }
            if (e_args.Button == MouseButtons.Right)
            {                
                ContextMenuStrip _cms = new ContextMenuStrip();
                ToolStripMenuItem _tsmi_Add = new ToolStripMenuItem();
                ToolStripMenuItem _tsmi_Edit = new ToolStripMenuItem();
                ToolStripMenuItem _tsmi_Delete = new ToolStripMenuItem();
                ToolStripSeparator _tss_Separator = new ToolStripSeparator();

                _tsmi_Add.Text = "Add New...";
                _tsmi_Add.Click += new EventHandler(_tsmi_Add_Click);
                _tsmi_Add.Enabled = allow_add;

                _tsmi_Edit.Text = "Edit";
                _tsmi_Edit.Click += new EventHandler(_tsmi_Edit_Click);
                _tsmi_Edit.Enabled = allow_edit;

                _tsmi_Delete.Text = "Delete";
                _tsmi_Delete.Click += new EventHandler(_tsmi_Delete_Click);
                _tsmi_Delete.Enabled = allow_delete;

                if (check_select)
                {
                    if (allow_add)
                        _cms.Items.Add(_tsmi_Add);
                    if (allow_edit)
                        _cms.Items.Add(_tsmi_Edit);
                    if (allow_delete)
                        _cms.Items.Add(_tsmi_Delete);
                    if (allow_add || allow_edit || allow_delete)
                        _cms.Items.Add(_tss_Separator);
                }
                else if (allow_add)
                    _cms.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { _tsmi_Add });
                else
                    return;
                _cms.RenderMode = System.Windows.Forms.ToolStripRenderMode.Professional;
                _cms.ShowImageMargin = false;
                _cms.Show((Control)sender, new Point(e_args.X, e_args.Y));
            }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Moves forward to the next section of dates. The number of days moved depends on the current mode. 
        /// (e.g. if mode is DAY the number of days is one, if mode is WEEK the number of days is seven, etc.)
        /// </summary>
        public void MoveToNext()
        {
            switch (mode)
            {
                case AvailabilityMode.Day:
                    start_date = start_date.AddDays(1.0);
                    break;
                case AvailabilityMode.Week:
                    if (start_date.DayOfWeek == DayOfWeek.Monday)
                        start_date = start_date.AddDays(7.0);
                    else
                    {
                        while (start_date.DayOfWeek != DayOfWeek.Monday)
                            start_date = start_date.AddDays(1.0);
                    }
                    break;
                case AvailabilityMode.Month:
                    start_date = new DateTime(start_date.Year, start_date.Month, 1, 0, 0, 0);
                    start_date = start_date.AddMonths(1);
                    break;
                case AvailabilityMode.Year:
                    start_date = new DateTime(start_date.Year, 1, 1, 0, 0, 0);
                    start_date = start_date.AddYears(1);
                    break;
            }
            RefreshControl(true);
        }
        /// <summary>
        /// Moves backwards to the previous section of dates. The number of days moved depends on the current mode. 
        /// (e.g. if mode is DAY the number of days is one, if mode is WEEK the number of days is seven, etc.)
        /// </summary>
        public void MoveToPrevious()
        {
            switch (mode)
            {
                case AvailabilityMode.Day:
                    start_date = start_date.AddDays(-1.0);
                    break;
                case AvailabilityMode.Week:
                    if (start_date.DayOfWeek == DayOfWeek.Monday)
                        start_date = start_date.AddDays(-7.0);
                    else
                    {
                        while (start_date.DayOfWeek != DayOfWeek.Monday)
                            start_date = start_date.AddDays(-1.0);
                        start_date = start_date.AddDays(-7.0);
                    }
                    break;
                case AvailabilityMode.Month:
                    start_date = new DateTime(start_date.Year, start_date.Month, 1, 0, 0, 0);
                    start_date = start_date.AddMonths(-1);
                    break;
                case AvailabilityMode.Year:
                    start_date = new DateTime(start_date.Year, 1, 1, 0, 0, 0);
                    start_date = start_date.AddYears(-1);
                    break;
            }
            RefreshControl(true);
        }
        /// <summary>
        /// Clears the selected contact(s)
        /// </summary>
        public void ClearSelection()
        {
            for (int i = 0; i < ContactCount; i++)
                if (contacts[i].Selected)
                    contacts[i].Selected = false;
        }
        /// <summary>
        /// Selects the contact(s) with the same name. 
        /// (if multiselect is set to false, only the last user with the same name is selected, and all current selections are cleared.)
        /// </summary>
        /// <param name="name">The name of the contact(s) to select</param>
        public void Select(string name)
        {
            for (int i = 0; i < ContactCount; i++)
            {
                if (name.Trim().ToUpper().Equals(contacts[i].Name.Trim().ToUpper()))
                    Select(i);
            }
        }
        /// <summary>
        /// Selected the contact at index.
        /// (if multiselect is set to false, all current selections are cleared.)
        /// </summary>
        /// <param name="index">the zero based index of the contact to select</param>
        public void Select(int index)
        {
            if (index >= ContactCount || index < 0)
                throw new IndexOutOfRangeException("Index is out of range. Must be between 0 and " + ContactCount);
            if (MutilSelect)
            {
                contacts[index].Selected = true;
            }
            else
            {
                ClearSelection();
                contacts[index].Selected = true;
            }
        }
        #endregion        

        

    }
    /// <summary>
    /// Display modes for Availability control.
    /// </summary>
    public enum AvailabilityMode
    {
        Day, Week, Month, Year
    }

    class AvailabilityPanel : Panel
    {
        List<decimal> list_start = new List<decimal> { };
        List<decimal> list_end = new List<decimal> { };
        List<Color> list_colour = new List<Color> { };
        List<string> list_tooltips = new List<string> { };

        public AvailabilityPanel()
        {
            this.Dock = DockStyle.Fill;
            this.Margin = new Padding(0);
            this.MouseClick += new MouseEventHandler(AvailabilityPanel_MouseClick);
        }

        void AvailabilityPanel_MouseClick(object sender, MouseEventArgs e)
        {
            for (int i = 0; i < list_start.Count; i++)
            {
                int start = Convert.ToInt32(this.Width * list_start[i]);
                int width = Convert.ToInt32(this.Width * (list_end[i] - list_start[i]));
                Rectangle task = new Rectangle(start, 0, width, 20);
                if (task.Contains(e.Location))
                    new ToolTip().Show(list_tooltips[i], this, e.X, e.Y, 5000);
            }
        }

        public void AddNewRectangle(decimal startPos, decimal endPos, Color colour, string tooltip)
        {
            if(startPos < 0 ||  startPos > 1)
                throw new ArgumentOutOfRangeException("start position must be between 0 and 1", "AvailabilityPanel control: error adding new rectangle");
            if(endPos < 0 ||  endPos > 1)
                throw new ArgumentOutOfRangeException("end position must be between 0 and 1", "AvailabilityPanel control: error adding new rectangle");
            if(startPos > endPos)
                throw new ArgumentOutOfRangeException("end position must be after start position", "AvailabilityPanel control: error adding new rectangle");
            list_start.Add(startPos);
            list_end.Add(endPos);
            list_colour.Add(colour);
            list_tooltips.Add(tooltip);
            this.Refresh();
        }

        public void ClearRectangles()
        {
            list_start = new List<decimal> { };
            list_end = new List<decimal> { };
            list_colour = new List<Color> { };
            list_tooltips = new List<string> { };
            this.Refresh();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            for (int i = 0; i < list_start.Count; i++)
            {
                int start = Convert.ToInt32(this.Width * list_start[i]);
                int width = Convert.ToInt32(this.Width * (list_end[i] - list_start[i]));
                e.Graphics.FillRectangle(new SolidBrush(list_colour[i]), new Rectangle(start, 0, width, 20));
            }
        }
    }
}
